package com.pg.data.gateway.utils;

import com.pg.data.gateway.dto.ChannelDTO;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * comtrade格式参考：https://blog.csdn.net/Mr_robot_strange/article/details/121905288
 * 使用方法：
 *      1. 创建Comtrade对象 Comtrade comtrade = new Comtrade();
 *      2. 调用read方法，传入cfg和dat文件路径
 *      3. 根据需要到Comtrade对象中获取数据
 * 注意： 提取的电流电压数据是已经经过a*x+b变换后的，不需要再进行a*x+b进行还原，
 * 如有需要，可以利用一次/二次变换比提取原始的电流电压物理量
 *
 */
public class Comtrade {

    private final String EXT_CFG = "cfg";
    private final String EXT_DAT = "dat";
    private final String EXT_INF = "inf";
    private final String EXT_HDR = "hdr";
    private final String ASCII_SEPARATOR = ",";

    // cfg文件和dat文件路径
    private String cfgFilePath = "";
    private String datFilePath = "";

    // 模拟量通道名
    private List<String> analogChannelIds = null;
    // 模拟量相位
    private List<String> analogPhases = null;
    // 状态量通道名
    private List<String> statusChannelIds = null;
    // 状态量相位
    private List<String> statusPhases = null;

    private Boolean timestampCritical = null;

    private Boolean useNumpyArrays = null;
    private Boolean useDoublePrecision = null;

    //
    private List<Double> timeValues = null;
    //  模拟量数据
    private List<List<Double>> analogValues = null;
    // 状态量数据
    private List<List<Integer>> statusValues = null;
    // 对应状态量通道是否存在变位（0->1 或者 1->0），1表示存在存在变位，0表示不存在
    private List<Integer> statusSwitch = null;

    // 额外的文件配置
    private String hrd = null;
    private String inf = null;

    private Cfg cfg = null;

    // 采样率，一个录波文件可能存在多个采样率，每个采样率是一个长度为2的数组，
    // arr[0]表示采样率， arr[1]表示当前采样率采样截至的点位
    private List<Double[]> sampleRates;

    private Integer totalSamples = 0;

    public Comtrade() {
        this.cfg = new Cfg();
        this.analogChannelIds = new ArrayList<>();
        this.analogPhases = new ArrayList<>();
        this.statusChannelIds = new ArrayList<>();
        this.statusPhases = new ArrayList<>();
        this.timestampCritical = false;
        this.useNumpyArrays = false;
        this.useDoublePrecision = false;
        this.timeValues = new ArrayList<>();
        this.analogValues = new ArrayList<>();
        this.statusValues = new ArrayList<>();
        this.statusSwitch = new ArrayList<>();
    }

    /**
     * 解析comtrade数据,数据保存在comtrade对象中
     * @param cfgFilePath cfg文件路径
     * @param datFilePath dat文件路径
     */
    public void read(String cfgFilePath, String datFilePath) {
        try {
            // 读取配置文件
            cfg.read(cfgFilePath);
            // 提取通道
            cfgExtractChannelsIds(cfg);
            // 提取相位
            cfgExtractPhases(cfg);
            // 读取解析DAT文件
            DatReader reader = getReader();
            reader.load(datFilePath, this.cfg, "UTF-8");
            // dat信息拷贝到Comtrade对象中
            datExtractData(reader);
        } catch (RuntimeException | IOException e) {
            throw new RuntimeException("Failed to parse comtrade file, cfgFilePath: %s".formatted(cfgFilePath));
        }

    }


    /**
     * 提取特定模拟通道原始数据值
     * @param channelId 模拟通道id 从1开始编号
     */
    public ChannelDTO extractSpecialAnalogChannel(int channelId) {
        if (channelId < 1) {
            throw new RuntimeException("Invalid channel id: " + channelId);
        }
        channelId -= 1;
        // 提取模拟通道数据
        List<Double> analogValue = this.analogValues.get(channelId);
        // 存储经过一二次变比转换后的模拟量数据
        List<Double> covertAnalogValue = new ArrayList<>();
        // 模拟通道的参数信息
        AnalogChannel analogChannel = cfg.getAnalogChannels().get(channelId);
        // 转换为原始值
        if (analogChannel.pors.toUpperCase().equals("S")) { // 二次值
            // 一次系数
            Double primary = analogChannel.primary;
            // 二次系数
            Double secondary = analogChannel.secondary;
            for (int i = 0; i < analogValue.size(); i++) {
                covertAnalogValue.add(Double.parseDouble(String.format("%.2f", primary * analogValue.get(i) / secondary)));
            }
        }
        ChannelDTO channelDTO = new ChannelDTO();
        channelDTO.setPhase(analogChannel.ph);
        // 判断是电流还是电压
        if (analogChannel.uu.toLowerCase().equals("a") || analogChannel.uu.toLowerCase().equals("ka")) {
            channelDTO.setCurrent(covertAnalogValue);
        } else if (analogChannel.uu.toLowerCase().equals("v") || analogChannel.uu.toLowerCase().equals("kv")) {
            channelDTO.setVoltage(covertAnalogValue);
        }
        return channelDTO;
    }

    /**
     * 获取每个模拟通道的配置信息
     *
     * @return
     */
    public List<AnalogChannel> getAnalogChannel() {
        return cfg.getAnalogChannels();
    }


    /**
     * 提取状态量通道
     *
     * @param channelId 通道id 从1开始编号
     * @param isStatus  true表示只提取有状态变化的通道， false表示全部提取
     * @return 状态通道数据
     */
    public ChannelDTO extractSpecialStatusChannel(int channelId, boolean isStatus) {
        if (channelId < 1) {
            throw new RuntimeException("Invalid channel id: " + channelId);
        }
        channelId -= 1;
        List<Integer> statusValue = this.statusValues.get(channelId);
        StatusChannel statusChannel = cfg.getStatusChannels().get(channelId);
        Set<Integer> statusSet = new HashSet<>();
        if (isStatus) {
            for (Integer status : statusValue) {
                statusSet.add(status);
            }
            if (statusSet.size() > 1) {
                ChannelDTO channelDTO = new ChannelDTO();
                channelDTO.setPhase(statusChannel.ph);
                channelDTO.setStatus(statusValue);
                return channelDTO;
            } else {
                return null;
            }
        } else {
            ChannelDTO channelDTO = new ChannelDTO();
            channelDTO.setPhase(statusChannel.ph);
            channelDTO.setStatus(statusValue);
            return channelDTO;
        }
    }

    public void datExtractData(DatReader datReader) {
        this.timeValues = datReader.time;
        this.analogValues = datReader.analog;
        this.statusValues = datReader.status;
        this.statusSwitch = datReader.statusSwitch;
        this.totalSamples = datReader.totalSamples;
        this.sampleRates = this.cfg.getSampleRates();

    }

    /**
     * 提取通道和相位信息
     *
     * @param cfg
     */
    public void cfgExtractChannelsIds(Cfg cfg) {
        cfg.getAnalogChannels().forEach(channel -> {
            this.analogChannelIds.add(channel.name);
        });
        cfg.getStatusChannels().forEach(channel -> {
            this.statusChannelIds.add(channel.name);
        });
    }

    /**
     * 提取相位信息
     *
     * @param cfg
     */
    public void cfgExtractPhases(Cfg cfg) {
        cfg.getAnalogChannels().forEach(channel -> {
            this.analogPhases.add(channel.ph);
        });
        cfg.getStatusChannels().forEach(channel -> {
            this.statusPhases.add(channel.ph);
        });
    }

    public DatReader getReader() {
        String ftUpper = ft().toUpperCase();
        if (ftUpper.equals(Constants.TYPE_ASCII)) {
            return new AsciiDatReader();
        } else if (ftUpper.equals(Constants.TYPE_BINARY)) {
            return new BinaryDatReader();
        } else {
            throw new RuntimeException("Unsupported file type: " + ftUpper);
        }
    }

    public List<String> getAnalogChannelIds() {
        return analogChannelIds;
    }

    public void setAnalogChannelIds(List<String> analogChannelIds) {
        this.analogChannelIds = analogChannelIds;
    }

    public List<String> getStatusChannelIds() {
        return statusChannelIds;
    }

    public void setStatusChannelIds(List<String> statusChannelIds) {
        this.statusChannelIds = statusChannelIds;
    }

    public List<List<Double>> getAnalogValues() {
        return analogValues;
    }

    public void setAnalogValues(List<List<Double>> analogValues) {
        this.analogValues = analogValues;
    }

    public List<List<Integer>> getStatusValues() {
        return statusValues;
    }

    public List<Integer> getStatusWitch() {
        return statusSwitch;
    }

    public void setStatusValues(List<List<Integer>> statusValues) {
        this.statusValues = statusValues;
    }

    /**
     * 获取start_time的微妙时间
     *
     * @return  start_time的微秒时间
     */
    public Long getStartTime() {
        // 转成微秒
        LocalDateTime startTimestamp = this.cfg.getStartTimestamp();
        Instant instant = startTimestamp.atZone(ZoneId.systemDefault()).toInstant();
        return instant.getEpochSecond() * 1_000_000 + (instant.getNano() / 1_000);
    }


    /**
     * Return the expected DAT file format
     *
     * @return
     */
    public String ft() {
        return this.cfg.getFt();
    }

    public List<Double[]> getSampleRates() {
        return sampleRates;
    }

    public Cfg getCfg() {
        return cfg;
    }

}



/**
 * 读取dat文件的抽象类
 */
abstract class DatReader {
    public String readMode = "r";
    public String revYear = "";
    public String filePath = "";
    public String content;
    public Cfg cfg;
    // 每个采样点对应的时刻
    public List<Double> time = new ArrayList<>();
    public List<List<Double>> analog = new ArrayList<>();
    public List<List<Integer>> status = new ArrayList<>();
    public List<Integer> statusSwitch = new ArrayList<>(); // 如果通道同时包含0和1，那说明有动作，则为1，否则为0
    public List<Integer> containerOne = new ArrayList<>(); // 一个通道的状态量是否包含1
    public List<Integer> containerZero = new ArrayList<>(); // 一个通道的状态量是否包含0
    public Integer totalSamples = 0;
    public Integer dataMissing;

    /**
     * Return the total samples (per channel)
     * @return
     */
    public Integer getTotalSamples() {
        return totalSamples;
    }

    /**
     * 加载并解析dat文件
     * @param datFilePath
     * @param cfg
     */
    public void load(String datFilePath, Cfg cfg, String encoding) throws IOException {
        this.filePath = datFilePath;
        File file = new File(datFilePath);
        if (!file.exists()) {
            throw new FileNotFoundException("File not found: " + filePath);
        }
        // 判断文件是否存在内容
        if (file.length() == 0) {
            throw new RuntimeException("File %s is empty".formatted(filePath));
        }
        Path path = Paths.get(datFilePath);
        this.cfg = cfg;
        prealLocate();
        if ((encoding == null || encoding.equals("")) && !readMode.equals("rb")) {
            encoding = "UTF-8";
        }
        if (readMode.equals("rb")) { // 字节流
            byte[] bytes = Files.readAllBytes(Paths.get(datFilePath));
            if (bytes == null || bytes.length == 0) {
                throw new RuntimeException("File content is empty: " + datFilePath);
            }
            // 文件内容解析
            this.parse(bytes);
        } else { // 字符流
            try (BufferedReader reader = Files.newBufferedReader(path, Charset.forName(encoding))) { // 字符流
                this.parse(reader);
            }
        }
    }

    /**
     * 内存预分配
     */
    public void prealLocate() {
        // 总采样的点位数
        double steps = this.cfg.getSampleRates().get(this.cfg.getSampleRates().size() - 1)[1];
        this.totalSamples = (int) (steps);
        // 模拟量通道的数量
        int analogCount = this.cfg.getAnalogCount();
        // 状态量通道的数量
        int statusCount = this.cfg.getStatusCount();

        for(int i = 0; i < this.totalSamples; i++) {
            this.time.add(0.0);
        }
        for(int i = 0; i < analogCount; i++) {
            this.analog.add(new ArrayList<>(Collections.nCopies(this.totalSamples, 0.0)));
        }
        for(int i = 0; i < statusCount; i++) {
            this.status.add(new ArrayList<>(Collections.nCopies(this.totalSamples, 0)));
            this.statusSwitch.add(0);
            this.containerZero.add(0);
            this.containerOne.add(1);
        }

    }

    /**
     *
     * @param n
     * @param tsValue
     * @param timeBase
     * @param timeMult
     * @return
     */
    public double getTime(long n, double tsValue, double timeBase, double timeMult) {
        double sampleRate = getSamp(n); // 采样率
        // 如果对时间的精度要求不高，则使用采样率计算时间戳
        if (!this.cfg.getTimestampCritical()) {
            if (sampleRate > 0) {
                return (n - 1) / sampleRate;
            } else {
                throw new RuntimeException("Missing timestamp and no sample rate provided.");
            }
        } else {
            return (tsValue * timeBase * timeMult);
        }

    }

    /**
     * 当前采样编号对应的采样率
     * @param n 采样编号
     * @return
     */
    public double getSamp(long n) {
        double lastSampleRate = 1.0;
        for (Double[] rate : cfg.getSampleRates()) {
            double samp = rate[0]; // 采样率
            double endSamp = rate[1];
            if (n <= endSamp) {
                return samp;
            }
        }
        return lastSampleRate;
    }

    /**
     * 处理缺省值
     * @param value
     * @return
     */
    public Double filterMissing(Long value) {
        if (value != null) {
            return value * 1.0;
        } else {
            return Double.NaN;
        }

    }

    /**
     * 子类实现具体的读取方式
     */
    public abstract void parse(Object content) throws IOException;


}

/**
 * 二进制dat文件解析
 */
class BinaryDatReader extends DatReader {

    private Integer ANALOG_BYTES = 2;
    private Integer STATUS_BYTES = 2;
    private Integer TIME_BYTES = 4;
    private Integer SAMPLE_NUMBER_BYTES = 4;

    // 二进制解码格式字符串
    private String STRUCT_FORMAT = "II %dh %dH";
    private String STRUCT_FORMAT_ANALOG_ONLY = "II %dh";
    private String STRUCT_FORMAT_STATUS_ONLY = "II %dH";

    public BinaryDatReader() {
        if (super.revYear.equals(Constants.REV_1991)) {
            super.dataMissing = -1; //  0xFFFF
        } else {
            super.dataMissing = -32768; // 0x8000
        }
        super.readMode = "rb";
    }


    /**
     * 解析 DAT 文件
     *
     * @param content DAT文件的字节数组
     */
    @Override
    public void parse(Object content) {
        Cfg cfg = super.cfg;
        Double timeMult = cfg.getTimeMultiplier();
        Double timeBase = cfg.getTimeBase();
        int analogCount = cfg.getAnalogCount();
        int statusCount = cfg.getStatusCount();

        List<Double> a = new ArrayList<>();
        List<Double> b = new ArrayList<>();
        // 线性变比系数
        cfg.getAnalogChannels().forEach(channel -> {
            a.add(channel.a);
            b.add(channel.b);
        });
        // 每一个状态通道使用0和1表示状态，这里使用bit表示所有通道状态，
        // 所以需要计算出需要多少个字节来表示所有状态，除以16是因为这里采用数字记录，一个数字占用两个字节
        int dbytes = STATUS_BYTES * (int) Math.ceil(statusCount / 16.0); // 占用字节数量
        int groups_of_16bits = (int) Math.floor(dbytes / STATUS_BYTES); // 2字节的数字个数

        if (!(content instanceof byte[])) {
            throw new RuntimeException("Invalid content type. Expected byte[].");
        }

        // 二进制解码
        Struct struct = new Struct((byte[]) content);
        // 定义二进制结构体格式
        struct.struct(getReaderFormat(analogCount, dbytes));
        int index = 0; // 索引
        for (List<Long> row : struct) {

            //采样编号，从1开始，每次递增1， 共totalSample个
            long n = row.get(0);
            // 当前采样的时间戳
            long tsVal = row.get(1);
            Double ts = getTime(n, tsVal, timeBase, timeMult);
            if (index >= this.totalSamples) {
                break;
            }
            this.time.set(index, ts);
            // Extract analog channel values.
            for (int i = 0; i < analogCount; i++) {
                Double yint = filterMissing(row.get(2 + i));
                if (yint.equals(Double.NaN)) {
                    this.analog.get(i).set(index, Double.NaN);
                } else {
                    Double y = a.get(i) * yint + b.get(i);
                    this.analog.get(i).set(index, y);
                }

            }
            // Extract status channel values.
            for (int i = 0; i < groups_of_16bits; i++) {
                Long group = row.get(2 + analogCount + i);
                // for each group of 16 bits, extract the status channels
                Integer maxchn = Math.min((i + 1) * 16, statusCount);
                for (int ichannel = i * 16; ichannel < maxchn; ichannel++) {
                    int chnindex = ichannel - i * 16;
                    int mask = 1 << chnindex;
                    int extract = (int) ((group & mask) >> chnindex);
                    this.status.get(ichannel).set(index, extract);
                    int one = this.containerOne.get(ichannel);
                    this.containerOne.set(ichannel, one & extract);
                    int zero = this.containerZero.get(ichannel);
                    this.containerZero.set(ichannel, zero | extract);
                    this.statusSwitch.set(ichannel, (this.containerOne.get(ichannel) ^ this.containerZero.get(ichannel)));
                }
            }
            index += 1;
        }

    }


    /**
     * 解码二进制数据时定义的结构体格式
     *
     * @param analog_channels 模拟通道数
     * @param status_bytes    状态通道字节数
     * @return
     */
    public String getReaderFormat(int analog_channels, int status_bytes) {
        int dCount = (int) Math.floor(status_bytes / 2.0);
        if (status_bytes > 0 && analog_channels > 0) {
            return String.format(STRUCT_FORMAT, analog_channels, dCount);
        } else if (analog_channels > 0) {
            return String.format(STRUCT_FORMAT_ANALOG_ONLY, analog_channels);
        } else {
            return String.format(STRUCT_FORMAT_STATUS_ONLY, dCount);
        }
    }
}


/**
 * dat采用ascii存储，对应的解析类
 */
class AsciiDatReader extends DatReader {

    // ascii格式的分隔符，默认为,
    private String ASCII_SEPARATOR = Constants.SEPARATOR;

    public AsciiDatReader() {
    }

    /**
     * 解析 DAT 文件
     *
     * @param content DAT文件的字节数组
     */
    @Override
    public void parse(Object content) throws IOException {
        Cfg cfg = super.cfg;
        Integer analogCount = cfg.getDigitalCount();
        Double timeMult = cfg.getTimeMultiplier();
        Double timeBase = cfg.getTimeBase();
        List<Double> a = new ArrayList<>();
        List<Double> b = new ArrayList<>();
        cfg.getAnalogChannels().forEach(channel -> {
            a.add(channel.a);
            b.add(channel.b);
        });
        if (!(content instanceof BufferedReader)) {
            throw new RuntimeException("Invalid content type. Expected BufferedReader.");
        }
        BufferedReader br = (BufferedReader) content;
        int lineNumber = 0;// 行号

            String line = null;
            while ((line = br.readLine()) != null) {
                lineNumber++;
                if (lineNumber > this.totalSamples) {
                    break;
                }
                // 读取一行数据
                String[] values = line.trim().split(this.ASCII_SEPARATOR);
                int n = Integer.parseInt(values[0]);
                double tsVal = Double.valueOf(values[1]);
                Double ts = getTime(n, tsVal, timeBase, timeMult);
                this.time.set(lineNumber - 1, ts);
                // 提取模拟通道
                for(int i = 2; i < analogCount + 2; i++) {
                    // 模拟通道值, 默认
                    Double aDouble = filterMissing(Long.valueOf(values[i]));
                    if (Double.isNaN(aDouble)) {
                        this.analog.get(i - 2).set(lineNumber - 1, Double.NaN);
                    } else {
                        double y = aDouble * a.get(i - 2) + b.get(i - 2);
                        this.analog.get(i - 2).set(lineNumber - 1, y);
                    }
                }
                // 提取状态通道
                for (int i = analogCount + 2; i < values.length; i++) {
                    // 状态通道值
                    int status = Integer.parseInt(values[i]);
                    this.status.get(i - analogCount - 2).set(lineNumber - 1, status);
                    int switches = this.statusSwitch.get(i - analogCount - 2);
                    this.statusSwitch.set(i - analogCount - 2, switches | status);
                }
            }

    }
}




/**
 * comtrade配置信息对象
 */
class Cfg {

     // time base units
    private final double TIME_BASE_NANO_SEC = 1E-9; // 纳秒
    private final double TIME_BASE_MICRO_SEC = 1E-6; // 微妙

    private String filePath = "";

    private Double timeBase = 0.0;

    // 站点名称
    private String stationName = "";

    // 记录装置特征号或者名称
    private String recDevId = "";

    // comtrade修订年份
    private String revYear = Constants.REV_2013;

    // 通道总数
    private int channelsCount = 0;

    // 模拟通道列表
    private List<AnalogChannel> analogChannels = null;

    // 状态通道列表
    private List<StatusChannel> statusChannels = null;

    // 模拟通道数量
    private int analogCount = 0;

    // 状态通道数量
    private int statusCount = 0;

    // 采样频率
    private Double frequency = 0.0;

    // 采样率数量
    private int nRates = 1;

    // 采样率列表 长度为2的数组，0表示采样率，1表示采样的节点数
    private List<Double[]> sampleRates = null;

    // 是否启用时间戳严格模式
    private boolean timestampCritical = false;

    // 初始化开始时间戳，默认为1900-01-01
    private LocalDateTime startTimestamp = LocalDateTime.of(1900, 1, 1, 0, 0);

    // 初始化触发时间戳，默认为1900-01-01
    private LocalDateTime triggerTimestamp = LocalDateTime.of(1900, 1, 1, 0, 0);

    // 设置文件类型为ASCII
    private String ft = Constants.TYPE_ASCII;

    // 初始化时间乘数因子
    private Double timeMultiplier = 1.0;

    // 2013标准修订信息：当timeCode和localCode均为0时，表示本地时间为UTC
    private String timeCode = "0";
    private String localCode = "0";


    // tmqCode 和 leapSecond 相关设置
    private String tmqCode = "0";
    private String leapSecond = "0";

    public Cfg() {
        this.analogChannels = new ArrayList<>();
        this.statusChannels = new ArrayList<>();
        this.sampleRates = new ArrayList<>();
    }

    public String getFilePath() {
        return filePath;
    }

    /**
     * 站名
     * @return
     */
    public String getStationName() {
        return stationName;
    }

    /**
     *记录设备id
     * @return
     */
    public String getRecDevId() {
        return recDevId;
    }

    /**
     *comtrade格式修订年份
     * @return
     */
    public String getRevYear() {
        return revYear;
    }

    /**
     * 返回通道总数
     * @return
     */
    public int getChannelsCount() {
        return channelsCount;
    }

    /**
     * 返回模拟通道列表
     * @return
     */
    public List<AnalogChannel> getAnalogChannels() {
        return analogChannels;
    }

    /**
     *返回状态通道列表
     * @return
     */
    public List<StatusChannel> getStatusChannels() {
        return statusChannels;
    }

    /**
     * 返回模拟通道数量
     * @return
     */
    public int getAnalogCount() {
        return analogCount;
    }

    /**
     *返回状态通道数量
     * @return
     */
    public int getStatusCount() {
        return statusCount;
    }

    /**
     * 返回时间基础单位
     * @return
     */
    public Double getTimeBase() {
        return timeBase;
    }

    /**
     * 返回频率
     * @return
     */
    public double getFrequency() {
        return frequency;
    }

    /**
     * Return the number of different sample rates within the DAT file.
     * @return
     */
    public int getnRates() {
        return nRates;
    }

    /**
     * Return a list with pairs describing the number of samples for a given
     *         sample rate.
     * @return
     */
    public List<Double[]> getSampleRates() {
        return sampleRates;
    }

    /**
     * Returns whether the DAT file must contain non-zero
     *          timestamp values.
     * dat文件是否必须包含非零时间戳
     * @return
     */
    public boolean getTimestampCritical() {
        return timestampCritical;
    }

    /**
     * Return the recording start time stamp as a datetime object
     * 记录的开始时间
     * @return
     */
    public LocalDateTime getStartTimestamp() {
        return startTimestamp;
    }

    /**
     * Return the trigger time stamp as a datetime object.
     * @return
     */
    public LocalDateTime getTriggerTimestamp() {
        return triggerTimestamp;
    }

    /**
     * 返回期望的dat文件的编码格式
     * @return
     */
    public String getFt() {
        return ft;
    }

    /**
     * 返回状态通道
     * @return
     */
    public List<StatusChannel> getDigitalChannels() {
        return statusChannels;
    }

    /**
     * 返回状态通道数量
     * @return
     */
    public int getDigitalCount() {
        return statusCount;
    }

    /**
     * 返回dat的时间乘数 默认为 1
     * @return
     */
    public Double getTimeMultiplier() {
        return timeMultiplier;
    }


    /**
     * Read CFG-format lines and stores its data.
     * @param filePath
     */
    public void read(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            throw new RuntimeException("File path is not existed");
        }
        if (file.length() == 0) {
            throw new RuntimeException("File is empty");
        }
        int lineCount = 0;

        try (BufferedReader br =  Files.newBufferedReader(Paths.get(filePath), Charset.forName("GB2312"))) {
            String line;
            // 第一行
            line = br.readLine();
            String[] packed = sepValues(line, -1);
            if (packed != null && packed.length == 3) {
                this.stationName = packed[0];
                this.recDevId = packed[1];
                this.revYear = packed[2];
                if (!this.revYear.equals(Constants.REV_1991) && !this.revYear.equals(Constants.REV_1999)
                        && !this.revYear.equals(Constants.REV_2001) && !this.revYear.equals(Constants.REV_2013)) {
                    throw new RuntimeException("Unknown comtrade version");
                }
            } else {
                // 如果没有指定版本号，默认是1991版本
                this.stationName = packed[0];
                this.recDevId = packed[1];
                this.revYear = Constants.REV_1991;
            }
            lineCount++;
            // 第二行 获取通道总数，模拟通道数，状态通道数
            line = br.readLine();
            packed = sepValues(line, 3);
            this.channelsCount = Integer.parseInt(packed[0]);
            this.analogCount = Integer.parseInt(packed[1].substring(0, packed[1].length() - 1));
            this.statusCount = Integer.parseInt(packed[2].substring(0, packed[2].length() - 1));
            lineCount++;

            // 下面analogCount行是模拟通道配置信息
            for (int i = 0; i < analogCount; i++) {
                line = br.readLine();
                packed = sepValues(line, 13);
                // 通道索引号，从1开始
                int n = Integer.parseInt(packed[0].trim());
                // 通道名称
                String name =packed[1];
                // 通道相位
                String ph = packed[2];
                // 被监视的回路元件
                String ccbm = packed[3];
                // 通道单位（kV,V,A,kA）
                String uu = packed[4];
                // 通道乘数
                Double a = Double.parseDouble(packed[5]);
                // 通道偏移数 电流电压的实际物理量的计算公式为：a*x+b x表示dat中存储的采样值
                Double b = packed[6].trim().equals("") ? 0.0 : Double.parseDouble(packed[6]);
                // 从抽样周期开始后的通道时滞
                Double skew = packed[7].trim().equals("") ? 0.0 : Double.parseDouble(packed[7]);
                // 该通道数据值的最小数据值（采样值）, 真实值需要进行转换
                Double cmin =Double.parseDouble(packed[8]);
                // 该通道数据值最大数据值范围（采样值） 真实值需要进行转换
                Double cmax =Double.parseDouble(packed[9]);
                // 电流或者电压变换比一次系数
                Double primary = Double.parseDouble(packed[10]);
                // 电流或者电压变换比二次系数
                Double secondary = Double.parseDouble(packed[11]);
                // 说明通道转换因子方程ax + b转换后的值是一次值还是二次值。必选。P：一次值。S：二次值。
                String pors = packed[12];
                this.analogChannels.add(new AnalogChannel(n, a, b, skew, cmin, cmax, name, uu, ph, ccbm, primary, secondary, pors));
                lineCount++;
            }

            // 下面statusCount行是状态通道配置信息
            for (int i = 0; i < statusCount; i++) {
                line = br.readLine();
                packed = sepValues(line, 5);
                int n = Integer.parseInt(packed[0].trim());
                String name = packed[1];
                String ph = packed[2];
                String ccbm = packed[3];
                int y = packed[4].trim().equals("") ? 0 : Integer.parseInt(packed[4]);
                this.statusChannels.add(new StatusChannel(n, name, ph, ccbm, y));
                lineCount++;
            }
            // 频率
            line = br.readLine();
            if (line.trim().length() > 0) {
                this.frequency = Double.parseDouble(line.strip());
            }
            lineCount++;
            // 采样率数量（一个文件可能配置多个采样率）
            line = br.readLine();
            this.nRates = Integer.parseInt(line.strip());
            if (this.nRates == 0) {
                this.nRates = 1;
                this.timestampCritical = true;
            } else {
                this.timestampCritical = false;
            }
            lineCount++;

            for (int i = 0; i < this.nRates; i++) {
                line = br.readLine();
                packed = sepValues(line, -1);
                // 采样频率
                Double samp = Double.parseDouble(packed[0].trim());
                // 在该采样频率下最后一次采样数
                Double endSamp = Double.parseDouble(packed[1].trim());
                this.sampleRates.add(new Double[] {samp, endSamp});
                lineCount++;
            }

            // First data point time and time base
            line = br.readLine();
            String tsStr = line.strip();
            Object[] objects = readTimestamp(tsStr, this.revYear);
            this.startTimestamp = (LocalDateTime) objects[0];
            boolean nanosec = (boolean) objects[1];
            this.timeBase = getTimeBase(nanosec);
            lineCount++;

            // Event data point and time base
            line = br.readLine();
            tsStr = line.strip();
            objects = readTimestamp(tsStr, this.revYear);
            this.triggerTimestamp = (LocalDateTime) objects[0];
            this.timeBase = Math.min(this.timeBase, getTimeBase((boolean) objects[1]));
            lineCount++;

            // DAT 文件类型，只能是ASCII，ascii BINARY binary
            line = br.readLine();
            this.ft = line.strip();
            lineCount++;

            // 时间戳标记乘数系数  从第一个采样点到任意一个其他采样点经过的时间计算公式为 timestamp（dat文件每行第二个字节表示的数） * timeMultiplier(单位为微妙)
            if (this.revYear.equals(Constants.REV_1999) || this.revYear.equals(Constants.REV_2001) || this.revYear.equals(Constants.REV_2013)) {
                line = br.readLine().trim();
                line = line.replace(String.valueOf((char)0x1A), "");
                if (line.length() > 0) {
                    this.timeMultiplier = Double.parseDouble(line);
                } else {
                    this.timeMultiplier = 1.0;
                }
                lineCount++;
            }

            // time_code and local_code
            if (this.revYear.equals(Constants.REV_2013)) {
                line = br.readLine().trim();
                line = line.replace(String.valueOf((char)0x1A), "");
                if (line != null) {
                    String[] strs = sepValues(line, -1);
                    this.timeCode = strs[0];
                    this.localCode = strs[1];
                    lineCount++;
                    // time_code and local_code
                    line = br.readLine().trim();
                    line = line.replace(String.valueOf((char)0x1A), "");
                    strs = sepValues(line, -1);
                    this.tmqCode = strs[0];
                    this.leapSecond = strs[1];
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 分割字符串
     * @param line
     * @param expected 期望分割后的字段数量
     * @return
     */
    public String[] sepValues(String line, int expected) {
        if (line != null) {
            String[] split = line.split(Constants.SEPARATOR);
            if (expected != -1 && split.length != expected) {
                throw new RuntimeException("Data format error");
            }
            String[] res = new String[split.length];
            for (int i = 0; i < split.length; i++) {
                res[i] = split[i].trim();
            }
            return res;
        }
        return null;
    }

    /**
     *  获取时间戳精度（微妙或者纳秒）
     * @param nanosec
     * @return
     */
    public Double getTimeBase(boolean nanosec) {
        if (nanosec) {
            return TIME_BASE_NANO_SEC;
        } else {
            return TIME_BASE_MICRO_SEC;
        }
    }


    /**
     * 提取时间戳信息
     * @param timestampLine
     * @param revYear
     * @return
     */
    public  Object[] readTimestamp(String timestampLine, String revYear) {
        int day = 0, month = 0, year = 0, hour = 0, minute = 0, second = 0, microsecond = 0;
        boolean nanoSec = false; // 指示时间戳是否精确到纳秒
        boolean usingMinData = false;

        if (!timestampLine.strip().isEmpty()) {
            String[] values = sepValues(timestampLine, -1);
            if (values.length >= 2) {
                String dateStr = values[0].strip();
                String timeStr = values[1].strip();

                if (!dateStr.isEmpty()) {
                    if (revYear.equals(Constants.REV_1991)) {
                        // 1991 Format Uses mm/dd/yyyy format
                        int[] date = parseDate(dateStr, Constants.DATE_PATTERN_1991);
                        month = date[0];
                        day = date[1];
                        year = date[2];
                    } else {
                        int[] date = parseDate(dateStr, Constants.DATE_PATTERN_MODERN);
                        day = date[0];
                        month = date[1];
                        year = date[2];
                    }
                }
                if (!timeStr.isEmpty()) {
                    int[] time = parseTime(timeStr);
                    hour = time[0];
                    minute = time[1];
                    second = time[2];
                    microsecond = time[3];
                    nanoSec = time[4] == 1;
                }
            }
        }
        if (year <= 0) {
            year = LocalDate.MIN.getYear();
            usingMinData = true;
        }
        if (month <= 0) {
            month = 1;
            usingMinData = true;
        }
        if (day <= 0) {
            day = 1;
            usingMinData = true;
        }
        LocalDateTime timestamp = LocalDateTime.of(year, month, day, hour, minute, second, microsecond * 1000);
        return new Object[]{timestamp, nanoSec};
    }

    /**
     * 提取日期信息
     * @param dateStr
     * @param pattern
     * @return
     */
    private static int[] parseDate(String dateStr, String pattern) {
        Pattern p = Pattern.compile(pattern);
        Matcher m = p.matcher(dateStr);
        if (m.matches()) {
            int day = Integer.parseInt(m.group(1));
            int month = Integer.parseInt(m.group(2));
            int year = Integer.parseInt(m.group(3));
            return new int[]{day, month, year};
        }
        return new int[]{0, 0, 0};
    }

    /**
     * 提取时间信息（年、月、日 小时 分 秒 微妙 纳秒）
     * @param timeStr
     * @return
     */
    private static int[] parseTime(String timeStr) {
        Pattern p = Pattern.compile(Constants.TIME_PATTERN);
        Matcher m = p.matcher(timeStr);
        if (m.matches()) {
            int hour = Integer.parseInt(m.group(1));
            int minute = Integer.parseInt(m.group(2));
            int second = Integer.parseInt(m.group(3));
            String fracSecStr = m.group(4);

            // Pad fraction of seconds with 0s to the right
            if (fracSecStr.length() <= 6) {
                // 长度小于6位，则用0补全
                fracSecStr = String.format("%-6s", fracSecStr).replace(' ', '0');
            } else {
                // 长度大于6位，用0补全9位
                fracSecStr = String.format("%-9s", fracSecStr).replace(' ', '0');
            }
            int fracSecond = Integer.parseInt(fracSecStr);
            // 是否是纳秒单位
            boolean inNanoseconds = fracSecStr.length() > 6;
            int microsecond = fracSecond;

            if (inNanoseconds) {
                // 如果是纳秒，转成微秒
                microsecond = (int) (microsecond * 1E-3);
            }
            return new int[]{hour, minute, second, microsecond, inNanoseconds ? 1 : 0};
        }
        return new int[]{0, 0, 0, 0, 0};
    }



}



class Channel {
    public int n;
    public String name;
    public String ph;
    public String ccbm;

    public Channel(int n, String name, String ph, String ccbm) {
        this.n = n;
        this.name = name;
        this.ph = ph;
        this.ccbm = ccbm;
    }

}

/**
 * 模拟量通道对象
 */
class AnalogChannel extends Channel {
    public String name; // 站名
    public String uu; // 通道单位（kV,V,A,kA）
    public int n; // 通道索引号 从1开始
    public Double a; // 转换系数
    public Double b; // 转换系数
    public Double skew;
    public Double cmin; // 该通道数据值的最小数据值（采样值）, 真实值需要进行转换
    public Double cmax; // 该通道数据值的最小数据值（采样值）, 真实值需要进行转换
    public String ph; // 通道相位
    public String ccbm; // 被监视的元器件
    // 这俩参数是电力系统的一次/二次比， 一次值可以简单理解成电流或者电压的实际值，
    // 二次值是一次值按照特定比例的缩小
    public Double primary; // 一次值
    public Double secondary; // 二次值
    public String pors; // 转换后的值是一次值还是二次值 S表示二次值，P表示一次值

    public AnalogChannel(int n,Double a, Double b, Double skew, Double cmin,
                         Double cmax, String name, String uu, String ph,
                         String ccbm, Double primary, Double secondary, String pors) {
        super(n, name, ph, ccbm);
        this.n = n;
        this.a = a;
        this.b = b;
        this.skew = skew;
        this.cmin = cmin;
        this.cmax = cmax;
        this.name = name;
        this.uu = uu;
        this.ph = ph;
        this.ccbm = ccbm;
        this.primary = primary;
        this.secondary = secondary;
        this.pors = pors;
    }
}

/**
 * 状态通道对象
 */
class StatusChannel extends Channel {
    public int n;
    public String name;
    public String ph;
    public String ccbm;
    public int y;

    public StatusChannel(int n, String name, String ph, String ccbm, int y) {
        super(n, name, ph, ccbm);
        this.name = name;
        this.ph = ph;
        this.y = y;
        this.ccbm = ccbm;
        this.n = n;
    }
}


/**
 * 常量对象
 */
class Constants {
    // COMTRADE standard revisions
    public static final String REV_1991 = "1991";
    public static final String REV_1999 = "1999";
    public static final String REV_2001 = "2001";
    public static final String REV_2013 = "2013";

    // DAT file format types
    public static final String TYPE_ASCII = "ASCII";
    public static final String TYPE_BINARY = "BINARY";
    public static final String TYPE_BINARY32 = "BINARY32";
    public static final String TYPE_FLOAT32 = "FLOAT32";

    // Special values
    public static final int TIMESTAMP_MISSING = 0xFFFFFFFF;

    // common separator character of data fields of CFG and ASCII DAT files
    public static final String SEPARATOR = ",";

    public static final String DATE_PATTERN_1991 = "(\\d{2})/(\\d{2})/(\\d{4})";
    public static final String DATE_PATTERN_MODERN = "(\\d{2})/(\\d{2})/(\\d{4})";
    public static final String TIME_PATTERN = "(\\d{2}):(\\d{2}):(\\d{2})\\.(\\d{1,9})";

}

